const { promisify } = require('util');
const { TimeoutError } = require('puppeteer');

/**
 * 页面基础操作类
 */
class PageHelper {
  constructor(page) {
    this.page = page;
  }
  //#region 基础方法 
  /** 
   * 页面等待
   */
  async pageWait(time) {
    const delay = promisify(setTimeout);
    await delay(time);
  }
  /**
   * 获取元素
   * @param {string} element 元素选择器名称
   * @param {number} timeout 超时时间
   * @param {boolean} noShowError 是否显示错误
   */
  async getElement(element, timeout = 30000, noShowError = false) {
    try {
      if (!element) return null;
      await this.page.waitForSelector(element, { visible: true, timeout: timeout });
      let res = await this.page.$(element);
      await this.pageWait(200);
      return res;
    } catch (error) {
      if (!noShowError) console.log('【错误】 ', element, error);
      if (error instanceof TimeoutError) {
        console.log('超时了');
      }
      return null;
    }
  }
  /**
   * 获取指定元素的文本
   * @param {*} element 
   * @param {*} timeout 
   * @param {*} noShowError 
   * @returns 
   */
  async getElementText(element, timeout = 30000, noShowError = false) {
    try {
      if (!element) return null;
      await this.page.waitForSelector(element, { visible: true, timeout: timeout });
      let res = await this.page.$(element);
      await this.pageWait(200);
      if (!res) return null;
      else {
        const innerText = await this.page.evaluate(el => el.innerText, res);
        return innerText
      }
    } catch (error) {
      if (!noShowError) console.log('【错误】 ', element, error);
      return null;
    }
  }
  /**
   * 等待页面加载到某个元素出现
   * @param {*} element 
   * @param {*} timeout    
   * @returns 
   */
  async pageLoadByElement(element, timeout = 30000) {
    try {
      if (!element) return;
      await this.page.waitForSelector(element, { timeout: timeout });
    } catch (error) {
      if (error instanceof TimeoutError) {
        console.log('超时了');
      } else console.log('【错误】 ', element, error);
    }
  }
  /**
   * 点击Radio选项
   * @param {string} element 元素选择器名称  
   * @param {string} title 步骤名称
   */
  async clickRadio(element, title) {
    if (title) console.log('正在选择：', title);
    try {
      const radioBtn = await this.getElement(element, step);
      radioBtn.click();
    } catch (error) {
      console.log('【错误】', title, error);
    }
  }

  /**
   * 等待页面加载完成
   */
  async waitForPageLoad(step, timeout = 30000) {
    this.checkStep(step);
    try {
      await this.page.waitForNavigation({ waitUntil: 'networkidle2', timeout: timeout });
    } catch (error) {
      // if (error.message.includes('TimeoutError')) console.log('等待超时')
      // else console.log('等待页面加载完成失败', error);
    }
  }
  /**
   * 判断元素是否存在并可见
   */
  async isElementExist(element, timeout = 1000) {
    try {
      const res = await this.page.waitForSelector(element, { visible: true, timeout: timeout });
      return res != null;
    } catch (error) {
      return false;
    }
  }
  /**
   * 不是使用打字机效果的输入方式
   */
  async setInputNoType(selector, value, title, timeout = 5000) {
    try {
      this.fillErrorMsg = '';
      if (title) console.log('正在填写：', title, '  填写内容：', value);
      const randomNum = Math.floor(Math.random() * 500) + 1;      
      await this.page.waitForSelector(selector, { visible: true, timeout: timeout });
      if (value) {
        await this.page.evaluate((sel, val) => {
          const input = document.querySelector(sel);
          if (input) {
            input.value = val;
          }
        }, selector, value);
      }
      await this.pageWait(randomNum);
    } catch (error) {
      if (error instanceof TimeoutError)
        console.log(`获取${selector}超时`);
      else
        console.log('【错误】', title, selector, value, error);
    }

  }
  async setInputByType(selector, value, title, timeout = 5000) {
    try {
      this.fillErrorMsg = '';
      if (title) console.log('正在填写：', title, '  填写内容：', value);
      const randomNum = Math.floor(Math.random() * 500) + 1;
      if (value) {
        const dateOfBirthInput = await this.getElement(selector, timeout);
        await dateOfBirthInput.click();
        await dateOfBirthInput.type(value);
      }
      await this.pageWait(randomNum);
    } catch (error) {
      console.log('【错误】', title, selector, value, error);
      this.fillErrorMsg = `填写${title}的时候，写入${value}失败`;
      throw new Error('fillError');
    }
  }

  /**
   * 根据文本选择下拉框   
   * @param {string} elementName 元素名称
   * @param {string} label select选项的文本
   * @param {string} title 选项标题
   * @param {number} step 当前步骤
   */
  async selectOptionByLabel(elementName, label, title, step, byclick = true) {
    this.checkStep(step);
    this.fillErrorMsg = '';
    if (title) console.log('正在填写：', title, '  填写内容：', label);
    const selectElement = await this.getElement(elementName, step);
    if (!selectElement) {
      console.log('【错误】', title, elementName, '(找不到页面元素)');
      this.fillErrorMsg = `填写${title}的时候，写入${label}失败，找不到页面元素`;
      throw new Error('fillError');
    }
    // 获取所有选项的文本值
    const options = await selectElement.$$eval('option', options => options.map(option => {
      return { text: option.text, value: option.value }
    }));
    // 找到包含相关label的选项
    const opValue = options.find(x => x.text.startsWith(label));
    if (opValue) {
      if (byclick) {
        // 点击 select 标签
        await this.page.click(elementName);
        // 等待 select 的 option 元素加载
        await this.page.waitForSelector(`${elementName} option[value="${opValue.value}"]`);
        await this.page.select(elementName, opValue.value);
      }
      else await selectElement.select(opValue.value); // 选中找到的选项
    }
    else {
      console.log('【错误】', title, label, '(找不到选项值)');
      this.fillErrorMsg = `填写${title}的时候，写入${label}失败，找不到选项值`;
      throw new Error('fillError');
    }
  }
  /**
   * 根据值Value选择下拉框   
   * @param {*} value 
   */
  async selectOptionByValue(elementName, value, title, step) {
    this.checkStep(step);
    this.fillErrorMsg = '';
    if (title) console.log('正在填写：', title, '  填写内容：', value);
    const selectElement = await this.getElement(elementName, step);
    if (!selectElement) {
      console.log('【错误】', title, elementName, '(找不到页面元素)');
      this.fillErrorMsg = `填写${title}的时候，写入${value}失败，找不到页面元素`;
      throw new Error('fillError');
    }
    await selectElement.select(value);
  }
  /**
   * 点击日期控件的日期表格
   * @param {string} dateData 日期ddmmyyyy     
   * @param {string} title 选项标题
   * @param {string} dateSelector 日期选择器
   * @param {string} yearSelector 年份选择器
   * @param {string} monthSelector 月份选择器
   * @param {number} step 当前步骤   
   * */
  async clickDayTable(dateData, title, dateSelector, yearSelector, monthSelector, step) {
    try {
      this.fillErrorMsg = '';
      const dateName = stringHelper.parseDateTime(dateData);
      const issueDateInput = await this.getElement(dateSelector, step);
      issueDateInput.click();
      await this.selectOptionByValue(yearSelector, dateData.slice(4, 8), title, step);
      await this.pageWait(500);
      await this.selectOptionByValue(monthSelector,
        (stringHelper.parseNumber(dateData, 2, 4) - 1) + '', title, step);
      await this.pageWait(500);
      await this.page.click(`[datepicker-label="${dateName}"]`);
    } catch (error) {
      console.log('【错误】', title, error);
      this.fillErrorMsg = `填写${title}的时候失败`;
      throw new Error('fillError');
    }

  }
  /**
   * 上传图片
   * @param {number} step 当前步骤
   */
  async uploadImage(step, ICAData) {
    this.checkStep(step);
    try {
      console.log('正在上传图片');
      await this.pageWait(500);
      const fileInput = await this.page.$('input[type=file]');
      await this.pageWait(100);
      // const filePath = path.join(__dirname, 'images/000.jpeg');
      await fileInput.uploadFile(ICAData.picFilePath);
      // await this.page.click('input[type=submit]');
      return true;
    } catch (error) {
      console.log('【上传图片错误】', error);
      return false;
    }
  }
  /**
   * 隐藏页面元素
   */
  async hideElement(selector) {
    await this.page.evaluate((sel) => {
      const button = document.querySelector(sel);
      if (button) {
        button.style.display = 'none';
      }
    }, selector);
  }
  /**
  * 显示页面元素
  */
  async showElement(selector) {
    await this.page.evaluate((sel) => {
      const element = document.querySelector(sel);
      if (element) {
        element.style.display = ''; // 将 display 属性设为默认值（例如：block、inline等）
      }
    }, selector);
  }
  //#endregion
}
module.exports = PageHelper;
